package tcln.gxtx.client.utils.lang;

/**
 * A string builder for use in GWT client-side code. This builder intends to provide an optimal way to apply the concept
 * of string building made by {@link StringBuilder} into client code.
 * <p>
 * To use this builder, use either one of the factory method <code>getInstance</code> to create an appropriate instance
 * of {@link GwtStringBuilder} to use. Typically, method {@link #getInstance()} is used, which use the default
 * implementation strategy provided by GWT SDK. In some extensive cases when concatentation requires so many calls to
 * <code>append</code> methods, JS native implementation would be better. In this case, JS native implementation would
 * be used via factory method {@link #getInstance(boolean)}.
 * 
 * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
 */
public abstract class GwtStringBuilder {

    /**
     * Returns a new implementation instance of this builder using default implementation strategy.
     * 
     * @return
     */
    public static GwtStringBuilder getInstance() {
        return getInstance(false);
    }

    /**
     * Returns a new implementation instance of this builder.
     * 
     * @param useNative
     *            <code>true</code> to force using native implementation of string builder, <code>false</code> to use
     *            the default strategy.
     * @return
     */
    public static GwtStringBuilder getInstance(boolean useNative) {
        return useNative ? new JsNativeStringBuilder() : new GoogleStringBuilder();
    }

    /**
     * Appends a character sequence (e.g. a {@link String}).
     * 
     * @param charSequence
     *            Character sequence to append. <code>null</code> or empty sequence is ignored.
     * @return This instance.
     */
    public final GwtStringBuilder append(CharSequence charSequence) {
        if (charSequence != null && charSequence.length() > 0) {
            appendInternal(charSequence.toString());
        }
        return this;
    }

    /**
     * Appends an integer.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(int v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>boolean</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(boolean v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>byte</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(byte v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a character.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(char v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>short</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(short v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>long</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(long v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>float</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(float v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends a <code>double</code>.
     * 
     * @param v
     * @return This instance.
     */
    public final GwtStringBuilder append(double v) {
        appendInternal(String.valueOf(v));
        return this;
    }

    /**
     * Appends an object.
     * 
     * @param object
     * @return This instance.
     */
    public final GwtStringBuilder append(Object object) {
        if (object != null) {
            appendInternal(object);
        }
        return this;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final String toString() {
        return toStringInternal();
    }

    protected abstract void appendInternal(Object object);

    protected abstract String toStringInternal();

    private static class GoogleStringBuilder extends GwtStringBuilder {
        private StringBuilder builder = new StringBuilder();

        @Override
        protected void appendInternal(Object object) {
            builder.append(object);
        }

        @Override
        protected String toStringInternal() {
            return builder.toString();
        }
    }

    private static class JsNativeStringBuilder extends GwtStringBuilder {
        private String whole = "";

        @Override
        protected void appendInternal(Object object) {
            whole += object.toString();
        }

        @Override
        protected String toStringInternal() {
            return whole;
        }
    }
}
